package com.wei.czz.framework.admin.manager;

import com.fasterxml.jackson.core.type.TypeReference;
import com.wei.czz.common.constant.Constant;
import com.wei.czz.common.constant.EntityConstant;
import com.wei.czz.common.constant.RedisConstant;
import com.wei.czz.common.dto.admin.RoleMenuManageDto;
import com.wei.czz.common.dto.admin.UserDto;
import com.wei.czz.common.dto.admin.role.RoleDto;
import com.wei.czz.common.dto.admin.role.RolePageEnumDto;
import com.wei.czz.common.dto.admin.roleMenu.MenuRoleDto;
import com.wei.czz.common.dto.admin.user.UserRoleDto;
import com.wei.czz.common.dto.admin.userRole.UserRoleViewDto;
import com.wei.czz.common.dto.common.UploadDto;
import com.wei.czz.common.easyexcel.listener.ObjectUploadListener;
import com.wei.czz.common.enums.CommonEnum;
import com.wei.czz.common.enums.ResultEnum;
import com.wei.czz.common.enums.admin.userRole.RelationModeEnum;
import com.wei.czz.common.enums.common.dict.DictEnum;
import com.wei.czz.common.enums.common.importTask.ImportTaskTypeEnum;
import com.wei.czz.common.enums.admin.dept.BusinessTypeEnum;
import com.wei.czz.common.exception.CzzException;
import com.wei.czz.common.po.UserPo;
import com.wei.czz.common.po.admin.userRole.UserRolePo;
import com.wei.czz.common.tool.PageDto;
import com.wei.czz.common.utils.CacheKeyUtils;
import com.wei.czz.common.utils.CopyUtils;
import com.wei.czz.common.utils.RandomNumUtils;
import com.wei.czz.common.utils.SecurityUtils;
import com.wei.czz.common.vo.admin.role.RoleUploadVo;
import com.wei.czz.common.vo.admin.userRole.UserRoleFormVo;
import com.wei.czz.framework.admin.entity.DeptUserEntity;
import com.wei.czz.framework.admin.entity.RoleEntity;
import com.wei.czz.framework.admin.entity.UserEntity;
import com.wei.czz.framework.admin.entity.UserRoleEntity;
import com.wei.czz.framework.admin.service.*;
import com.wei.czz.framework.common.entity.DictEntity;
import com.wei.czz.framework.common.helper.ImportTaskHelper;
import com.wei.czz.framework.common.handler.redis.RedisHandler;
import com.wei.czz.framework.common.handler.redis.RedisStringHandler;
import com.wei.czz.common.vo.admin.role.RoleVo;
import com.wei.czz.common.vo.common.UpdateStatusVo;
import com.wei.czz.common.vo.admin.role.RoleFormVo;
import com.wei.czz.framework.common.helper.CacheHelper;
import com.wei.czz.framework.common.handler.ThrottleHandler;
import com.wei.czz.framework.common.service.TransactionService;
import com.wei.czz.framework.common.wrapper.DictWrapper;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2023-12-07 17:30:02
 * className: RoleManager
 * version: 1.0
 * description:
 */
@Component
@AllArgsConstructor
public class RoleManager {

    private static final Logger log = LoggerFactory.getLogger(RoleManager.class);

    private final UserService userService;

    private final RoleService roleService;

    private final DeptService deptService;

    private final RoleMenuService roleMenuService;

    private final UserRoleService userRoleService;

    private final DeptUserService deptUserService;

    private final DeptRoleService deptRoleService;

    private final TransactionService transactionService;

    private final RedisStringHandler redisStringHandler;

    private final RedisHandler redisHandler;

    private final RedissonClient redissonClient;

    private final CacheHelper cacheHelper;

    private final ThrottleHandler throttleHandler;

    private final ImportTaskHelper importTaskHelper;

    private final DictWrapper dictWrapper;

    @Transactional
    public void saveRole(RoleVo roleVo) {
        // 获取操作用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        List<RoleEntity> roleList = roleService.findList(Collections.singletonList(roleVo.getRoleKey()));
        if (!roleList.isEmpty()) {
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "已存在相同的角色键的角色，添加失败。");
        }

        /*
            保存角色
         */
        RoleEntity role = new RoleEntity();
        role.setRoleName(roleVo.getRoleName())
                .setRoleKey(roleVo.getRoleKey())
                .setSort(roleVo.getSort())
                .setStatus(CommonEnum.ZERO.getValue())
                .setDeleteStatus(CommonEnum.ZERO.getValue())
                .setUpdateTime(date)
                .setUpdateUser(StringUtils.EMPTY)
                .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                .setCreateTime(date)
                .setCreateUser(userPo.getUsername())
                .setCreateUserId(userPo.getUserId())
                .setRemark(Optional.ofNullable(roleVo.getRemark()).orElse(StringUtils.EMPTY));
        roleService.saveOrEdit(role);

        // 删除系统角色相关的所有临时缓存
        this.deleteRoleCache();
    }

    public UploadDto<RoleUploadVo> uploadRole(ObjectUploadListener<RoleUploadVo> objectUploadListener) {
        // 获取操作用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        ImportTaskTypeEnum importTaskTypeEnum = ImportTaskTypeEnum.ROLE;

        // 解析成功列表
        List<RoleUploadVo> successList = objectUploadListener.getSuccessList();
        // 解析失败列表
        List<RoleUploadVo> failList = objectUploadListener.getFailList();

        /*
            判断是否有可导入数据
         */
        if (successList.isEmpty()) {
            // 没有解析成功数据，直接返回
            return importTaskHelper.buildSyncUploadDto(importTaskTypeEnum, successList, failList, RoleUploadVo.class);
        }
        // 收集角色键
        List<String> roleKeyList = CopyUtils.mapList(successList, RoleUploadVo::getRoleKey);

        /*
            获取角色
         */
        List<RoleEntity> dbRoleList = roleService.findList(roleKeyList);
        // 收集角色键
        Set<String> roleKeySet = CopyUtils.mapSet(dbRoleList, RoleEntity::getRoleKey);

        List<RoleEntity> saveList = new ArrayList<>();

        Map<String, Integer> businessTypeNameMap = BusinessTypeEnum.getNameMap();

        Iterator<RoleUploadVo> iterator = successList.iterator();
        while (iterator.hasNext()) {

            RoleUploadVo roleUploadVo = iterator.next();
            if (roleKeySet.contains(roleUploadVo.getRoleKey())) {
                log.info("角色键已存在。serial={} roleKey={}", roleUploadVo.getSerial(), roleUploadVo.getRoleKey());
                roleUploadVo.setErrorMessage("角色键已存在");
                failList.add(roleUploadVo);
                iterator.remove();
                continue;
            }
            // 获取业务类型
            Integer businessType = businessTypeNameMap.get(roleUploadVo.getBusinessTypeName());
            if (Objects.isNull(businessType)) {
                log.info("业务类型不存在。serial={} businessTypeName={}", roleUploadVo.getSerial(),
                        roleUploadVo.getBusinessTypeName());
                roleUploadVo.setErrorMessage("业务类型不存在");
                failList.add(roleUploadVo);
                iterator.remove();
                continue;
            }

            // 映射
            RoleEntity role = CopyUtils.map(roleUploadVo, RoleEntity.class);
            role.setBusinessType(businessType)
                    .setStatus(CommonEnum.ZERO.getValue())
                    .setDeleteStatus(CommonEnum.ZERO.getValue())
                    .setUpdateTime(date)
                    .setUpdateUser(StringUtils.EMPTY)
                    .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                    .setCreateTime(date)
                    .setCreateUser(userPo.getUsername())
                    .setCreateUserId(userPo.getUserId())
                    .setRemark(Optional.ofNullable(roleUploadVo.getRemark()).orElse(StringUtils.EMPTY));
            saveList.add(role);
        }

        /*
            生成导入记录
         */
        UploadDto<RoleUploadVo> uploadDto = importTaskHelper.buildSyncUploadDto(importTaskTypeEnum, successList,
                failList, RoleUploadVo.class);

        if (saveList.isEmpty()) {
            log.info("映射成功数据为空");
            return uploadDto;
        }

        transactionService.execute(() -> {
            /*
                保存角色
             */
            roleService.batchSave(saveList);
        });

        // 删除系统角色相关的所有临时缓存
        this.deleteRoleCache();

        return uploadDto;
    }

    public PageDto<RoleDto> getPageDeptRoleList(RoleFormVo roleVo) {
        // 获取请求用户主键
        Long optUserId = SecurityUtils.getUserId();

        /*
            缓存请求参数
         */
        String cacheKey = CacheKeyUtils.getParamCacheKey(EntityConstant.ROLE);
        cacheHelper.cache(cacheKey, roleVo);

        String roleListKey = roleVo.getRoleListKey();

        // 查询Redis缓存，获取角色分页数据
        PageDto<RoleDto> pageDto = redisStringHandler.get(roleListKey);
        if (pageDto != null) {
            throttleHandler.throttleRun(() -> {
                // 尝试更新缓存键的过期时间
                redisHandler.tryUpdateKeyExpire(roleListKey, 150, RandomNumUtils.random5Minutes(10));
            }, roleListKey, 1500, TimeUnit.MILLISECONDS);
            return pageDto;
        }
        // 获取Redis分布式锁对象
        RLock lock = redissonClient.getLock(RedisConstant.LOCK + roleListKey);
        // 加锁
        lock.lock();
        try {
            // 重复查询Redis缓存，获取角色分页数据
            pageDto = redisStringHandler.get(roleListKey);
            if (pageDto != null) {
                log.info("double check获取到数据");
                return pageDto;
            }

            /*
                分页查询角色
             */
            PageDto<RoleEntity> rolePageDto = roleService.getPageList(roleVo);
            if (rolePageDto.isEmpty()) {
                log.info("分页获取角色结果为空");
                pageDto = rolePageDto.rebuild();
                // 缓存数据5分钟
                redisStringHandler.set(roleListKey, pageDto, RandomNumUtils.random5Minutes(10), TimeUnit.SECONDS);

                return pageDto;
            }

            List<RoleEntity> roleList = rolePageDto.getList();

            // 列表映射
            BiConsumer<RoleEntity, RoleDto> biConsumer = (role, roleDto) -> {
                roleDto.setBusinessTypeName(BusinessTypeEnum.getName(role.getBusinessType()))
                        .setStatusName(CommonEnum.getStatusName(role.getStatus()));
            };
            List<RoleDto> roleDtoList = CopyUtils.mapList(roleList, RoleDto.class, biConsumer);
            // 数据封装
            pageDto = rolePageDto.rebuild(roleDtoList);

            // 缓存数据5分钟
            redisStringHandler.set(roleListKey, pageDto, RandomNumUtils.random5Minutes(10), TimeUnit.SECONDS);

            return pageDto;
        } finally {
            // 判断当前锁对象是否还持有锁 并 持有该锁的线程与当前业务线程一致
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                // 解锁
                lock.unlock();
            }
        }
    }

    public RolePageEnumDto getRolePageParam() {
        // 获取请求用户主键
        Long optUserId = SecurityUtils.getUserId();

        /*
            获取页面参数
         */
        String cacheKey = CacheKeyUtils.getParamCacheKey(EntityConstant.ROLE);
        RoleFormVo roleFormVo = cacheHelper.get(cacheKey, RoleFormVo.class);

        RolePageEnumDto rolePageEnumDto = new RolePageEnumDto();
        rolePageEnumDto.setRoleFormVo(roleFormVo)
                .setBusinessTypeList(BusinessTypeEnum.getList())
                .setStatusList(CommonEnum.getStatusList());
        return rolePageEnumDto;
    }

    public List<RoleDto> getDictRoleList(DictEnum dictEnum) {
        /*
            获取字典列表
         */
        List<DictEntity> dictList;
        if (dictEnum.getMultiFlag()) {
            // 获取字典列表
            dictList = dictWrapper.getDictList(dictEnum);
        } else {
            // 获取字典
            DictEntity dict = dictWrapper.getOneDict(dictEnum);

            dictList = Collections.singletonList(dict);
        }
        List<Long> roleIdList = dictList.stream()
                .map(dict -> {
                    try {
                        return Long.parseLong(dict.getValue());
                    } catch (NumberFormatException e) {
                        log.error("字典配置错误。{} message={}", dict, e.getMessage(), e);
                        return null;
                    }
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        /*
            获取角色
         */
        List<RoleEntity> roleList = roleService.getList(roleIdList);
        if (roleList.isEmpty()) {
            log.info("字典配置的角色不存在。roleIdList={}", roleIdList);
            return Collections.emptyList();
        }
        // 封装返回
        BiConsumer<RoleEntity, RoleDto> biConsumer = (role, roleDto) -> {
            roleDto.setBusinessTypeName(BusinessTypeEnum.getName(role.getBusinessType()))
                    .setStatusName(CommonEnum.getStatusName(role.getStatus()));
        };
        return CopyUtils.mapList(roleList, RoleDto.class, biConsumer);
    }

    public List<RoleDto> getRoleList() {

        String cacheKey = RedisConstant.STRING + EntityConstant.ROLE + RedisConstant.LIST;

        List<RoleDto> roleDtoList = cacheHelper.get(cacheKey, new TypeReference<List<RoleDto>>() {});
        if (Objects.nonNull(roleDtoList)) {
            return roleDtoList;
        }

        String lockKey = RedisConstant.LOCK + cacheKey;
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();

        try {
            // double check
            roleDtoList = cacheHelper.get(cacheKey, new TypeReference<List<RoleDto>>() {});
            if (Objects.nonNull(roleDtoList)) {
                log.info("double check 获取到数据");
                return roleDtoList;
            }

            List<RoleEntity> roleList = roleService.getList();

            // 列表映射
            roleDtoList = CopyUtils.mapList(roleList, RoleDto.class);

            // 缓存数据5分钟
            cacheHelper.cache(cacheKey, roleDtoList, RandomNumUtils.random5Minutes(10), TimeUnit.SECONDS);

            return roleDtoList;

        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    public MenuRoleDto getMenuRoleRelationData(Long menuId) {

        /*
            获取菜单已关联的角色列表
         */
        List<Long> roleIdList = roleMenuService.findRoleIdList(menuId);

        /*
            获取角色
         */
        List<RoleEntity> roleList = roleService.getList(roleIdList);

        // 列表映射
        List<RoleMenuManageDto> menuRoleList = CopyUtils.mapList(roleList, RoleMenuManageDto.class);

        /*
            获取所有可用角色
         */
        roleList = roleService.getList();

        // 列表映射
        List<RoleMenuManageDto> roleDtoList = CopyUtils.mapList(roleList, RoleMenuManageDto.class);

        MenuRoleDto menuRoleDto = new MenuRoleDto();
        menuRoleDto.setRoleList(roleDtoList)
                .setMenuRoleList(menuRoleList);
        return menuRoleDto;
    }

    @Transactional
    public void updateRole(RoleVo roleVo) {
        // 获取操作用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        String lockKey = RedisConstant.LOCK + EntityConstant.ROLE + Constant.SPLIT + roleVo.getRoleId();
        // 获取分布式锁对象
        RLock lock = redissonClient.getLock(lockKey);
        // 加锁
        boolean bool = lock.tryLock();
        log.info("修改角色，尝试加锁完成。lockKey={} bool={}", lockKey, bool);
        if (!bool) {
            String message = String.format("‘%s’角色正在被修改，此次操作被拒绝，您可以稍后再试", roleVo.getRoleName());
            throw new CzzException(ResultEnum.TRY_LOCK_FAIL.getCode(), message);
        }

        try {
            /*
                获取角色列表
             */
            List<RoleEntity> roleList = roleService.findList(Collections.singletonList(roleVo.getRoleKey()));
            RoleEntity _role = roleList.stream().filter(role -> !role.getRoleId().equals(roleVo.getRoleId()))
                    .findFirst().orElse(null);
            if (Objects.nonNull(_role)) {
                log.info("角色键相同。roleId={} _roleId={}", roleVo.getRoleId(), _role.getRoleId());
                throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "已存在相同的角色键的角色，修改失败");
            }

            /*
                修改角色
             */
            RoleEntity updateRole = new RoleEntity();
            updateRole.setRoleId(roleVo.getRoleId())
                    .setRoleName(roleVo.getRoleName())
                    .setRoleKey(roleVo.getRoleKey())
                    .setSort(roleVo.getSort())
                    .setRemark(Optional.ofNullable(roleVo.getRemark()).orElse(StringUtils.EMPTY))
                    .setUpdateTime(date)
                    .setUpdateUser(userPo.getUsername())
                    .setUpdateUserId(userPo.getUserId());
            roleService.saveOrEdit(updateRole);

            // 删除系统角色相关的所有临时缓存
            this.deleteRoleCache();

        } finally {
            // 判断当前锁对象是否还持有锁 并 持有该锁的线程与当前业务线程一致
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                // 解锁
                lock.unlock();
            }
        }
    }

    public void updateRoleStatus(UpdateStatusVo updateStatusVo) {
        // 获取操作用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        /*
            修改角色
         */
        RoleEntity updateRole = new RoleEntity();
        updateRole.setRoleId(updateStatusVo.getIdList().get(0))
                .setStatus(updateStatusVo.getStatus())
                .setUpdateTime(date)
                .setUpdateUser(userPo.getUsername())
                .setUpdateUserId(userPo.getUserId());
        roleService.saveOrEdit(updateRole);

        // 删除系统角色相关的所有临时缓存
        this.deleteRoleCache();
    }

    @Transactional
    public void deleteRole(List<Long> roleIdList) {
        /*
            删除角色
         */
        roleService.delete(roleIdList);

        /*
            删除角色关联用户
         */
        userRoleService.deleteUserRole(Collections.emptyList(), roleIdList);

        /*
            删除角色关联菜单
         */
        roleMenuService.delete(roleIdList, null);

        /*
            删除角色关联部门
         */
        deptRoleService.deleteDeptRole(null, null, roleIdList);

        // 删除系统角色相关的所有临时缓存
        this.deleteRoleCache();
    }

    public List<UserRoleDto> getRoleUserList(Long roleId) {

        /*
            获取用户与角色关联数据
         */
        List<UserRoleEntity> userRoleList = userRoleService.findList(null, roleId, CommonEnum.ZERO.getValue());
        // 收集用户主键
        List<Long> userIdList = CopyUtils.mapList(userRoleList, UserRoleEntity::getUserId);

        /*
            获取用户
         */
        List<UserEntity> userList = userService.getList(userIdList);
        // 用户映射
        Map<Long, UserEntity> userMap = CopyUtils.listToMap(userList, UserEntity::getUserId, Function.identity());

        List<UserRoleDto> userRoleDtoList = new ArrayList<>(userRoleList.size());
        for (UserRoleEntity userRole : userRoleList) {
            UserRoleDto userRoleDto = new UserRoleDto();

            UserEntity user = userMap.get(userRole.getUserId());
            if (user == null) {
                log.info("角色关联用户不存在。userId={}", userRole.getUserId());
                continue;
            }
            userRoleDto.setRelationId(userRole.getId().toString())
                    .setUserId(userRole.getUserId().toString())
                    .setUsername(user.getUsername())
                    .setPhone(user.getPhone())
                    .setEmail(user.getEmail())
                    .setLoginName(user.getLoginName())
                    .setCreateTime(userRole.getCreateTime())
                    .setCreateUser(userRole.getCreateUser());

            userRoleDtoList.add(userRoleDto);
        }
        return userRoleDtoList;
    }

    public List<UserRoleViewDto> getUserRoleList(Long userId) {

        /*
            获取用户关联角色
         */
        List<UserRolePo> roleList = roleService.findList(userId, null);
        // 列表映射
        return roleList.stream()
                .sorted(
                        Comparator.comparing(UserRolePo::getRelationMode)
                                .thenComparing(UserRolePo::getUpdateTime)
                                .thenComparing(UserRolePo::getSort)
                )
                .map(userRolePo -> CopyUtils.map(userRolePo, UserRoleViewDto.class))
                .peek(userRoleViewDto -> {
                    userRoleViewDto.setRelationModeName(RelationModeEnum.getName(userRoleViewDto.getRelationMode()));
                })
                .collect(Collectors.toList());
    }

    public List<String> getUserRoleIdList(Long userId) {
        /*
            获取用户关联角色主键
         */
        return userRoleService.getRoleIdList(userId);
    }

    public PageDto<UserDto> getRoleUserPageList(UserRoleFormVo formVo) {

        /*
            分页获取角色关联用户
         */
        PageDto<UserEntity> pageDto = userService.getRoleUserPageList(formVo);
        if (pageDto.isEmpty()) {
            return pageDto.rebuild();
        }

        List<UserEntity> userList = pageDto.getList();
        // 收集用户主键
        List<Long> userIdList = CopyUtils.mapList(userList, UserEntity::getUserId);

        /*
            获取用户关联部门
         */
        List<DeptUserEntity> deptUserList = deptUserService.findList(null, userIdList);
        // 收集部门主键
        List<Long> deptIdList = new ArrayList<>();
        Map<Long, List<Long>> deptUserMap = new HashMap<>();
        for (DeptUserEntity deptUser : deptUserList) {
            deptIdList.add(deptUser.getDeptId());
            List<Long> _deptIdList = deptUserMap.computeIfAbsent(deptUser.getUserId(), k -> new ArrayList<>());
            _deptIdList.add(deptUser.getDeptId());
        }
        log.info("deptIdList={}", deptIdList);
        log.info("deptUserMap={}", deptUserMap);

        /*
            获取部门名称映射
         */
        Map<Long, String> deptNameMap = deptService.getDeptNameMap(deptIdList, Function.identity());
        log.info("deptNameMap={}", deptNameMap);

        StringBuilder sb = new StringBuilder();

        List<UserDto> list = new ArrayList<>();
        for (UserEntity user : userList) {
            sb.delete(0, sb.length());

            deptIdList = deptUserMap.getOrDefault(user.getUserId(), Collections.emptyList());
            for (Long deptId : deptIdList) {
                String deptName = deptNameMap.get(deptId);
                if (StringUtils.isNotBlank(deptName)) {
                    sb.append(deptName).append("、");
                }
            }
            if (sb.length() > 0) {
                sb.deleteCharAt(sb.length() - 1);
            }

            UserDto userDto = new UserDto();
            userDto.setUserId(user.getUserId().toString())
                    .setDeptNames(sb.toString())
                    .setUsername(user.getUsername())
                    .setStatus(user.getStatus())
                    .setStatusName(CommonEnum.getStatusName(user.getStatus()));

            list.add(userDto);
        }
        // 封装返回
        return pageDto.rebuild(list);
    }

    /**
     * 删除系统角色相关的所有临时缓存
     */
    private void deleteRoleCache() {
        String[] patternKeys = {
                RedisConstant.STRING + EntityConstant.ROLE + Constant.UNDER + RedisConstant.LIST
                        + RedisConstant.ASTERISK
        };
        redisHandler.fuzzyDelete(patternKeys);
    }

}
